package cn.oyzh.store.jdbc;

import cn.oyzh.common.util.CollectionUtil;
import cn.oyzh.store.jdbc.h2.H2StandardOperator;
import cn.oyzh.store.jdbc.sqlite.SqliteStandardOperator;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author oyzh
 * @since 2024-09-23
 */
public abstract class JdbcStandardStore<M extends Serializable> extends JdbcStore<M> {

    private final JdbcStandardOperator operator;

    public JdbcStandardStore() {
        try {
            TableDefinition tableDefinition = this.tableDefinition();
            if (JdbcManager.dialect == JdbcDialect.H2) {
                this.operator = new H2StandardOperator(tableDefinition);
            } else {
                this.operator = new SqliteStandardOperator(tableDefinition);
            }
            this.operator.initTable();
            this.init();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }

    @Override
    protected TableDefinition tableDefinition() {
        if (this.operator != null) {
            return this.operator.getTableDefinition();
        }
        return TableDefinition.ofClass(this.modelClass());
    }

    public boolean insert(M model) {
        if (model != null) {
            try {
                // 处理主键值
                this.tableDefinition().handlePrimaryKeyValue(model);
                return this.operator.insert(this.toRecord(model)) > 0;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }

    public boolean update(M model) {
        if (model != null) {
            try {
                TableDefinition tableDefinition = this.tableDefinition();
                PrimaryKeyColumn primaryKey = tableDefinition.primaryKeyColumn(model);
                if (primaryKey == null) {
                    return false;
                }
                Map<String, Object> record = this.toRecord(model);
                return this.operator.update(record, primaryKey) > 0;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }

    public boolean exist(Object primaryKey) {
        if (primaryKey != null) {
            try {
                return this.operator.exist(primaryKey);
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }

    public boolean exist(Map<String, Object> params) {
        try {
            return this.operator.exist(params);
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public M selectOne(Object primaryKey) {
        try {
            return this.toModel(this.operator.selectOne(primaryKey));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public M selectOne(SelectParam selectParam) {
        try {
            return this.toModel(this.operator.selectOne(selectParam));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public M selectOne(QueryParam queryParam) {
        try {
            return this.toModel(this.operator.selectOne(queryParam));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public List<M> selectList() {
        return this.selectList(SelectParam.EMPTY);
    }

    public List<M> selectList(QueryParam queryParam) {
        SelectParam param = new SelectParam();
        param.addQueryParam(queryParam);
        return this.selectList(param);
    }

    public List<M> selectList(SelectParam param) {
        try {
            List<Map<String, Object>> list = this.operator.selectList(param);
            if (CollectionUtil.isNotEmpty(list)) {
                List<M> models = new ArrayList<>();
                for (Map<String, Object> map : list) {
                    models.add(this.toModel(map));
                }
                return models;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return new ArrayList<>();
    }

    public long selectCount(QueryParam queryParam) {
        try {
            return this.operator.selectCount(List.of(queryParam));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return 0L;
    }

    public long selectCount(List<QueryParam> params) {
        try {
            return this.operator.selectCount(params);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return 0L;
    }

    public long selectCount(String kw, List<String> columns) {
        return this.selectCount(kw, columns, null);
    }

    public long selectCount(String kw, List<String> columns, QueryParams queryParams) {
        try {
            return this.operator.selectCount(kw, columns, queryParams);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return 0L;
    }

    public List<M> selectPage(String kw, List<String> columns, PageParam pageParam) {
        try {
            List<Map<String, Object>> list = this.operator.selectPage(kw, columns, pageParam);
            if (CollectionUtil.isNotEmpty(list)) {
                List<M> models = new ArrayList<>();
                for (Map<String, Object> map : list) {
                    models.add(this.toModel(map));
                }
                return models;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return Collections.emptyList();
    }

    public boolean delete(M model) {
        if (model != null) {
            try {
                Object primaryKeyValue = this.tableDefinition().getPrimaryKeyValue(model);
                return this.operator.delete(primaryKeyValue) > 0;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }

    public boolean delete(Object primaryKey) {
        if (primaryKey != null) {
            try {
                return this.operator.delete(primaryKey) > 0;
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return false;
    }

    public boolean delete(DeleteParam deleteParam) {
        try {
            return this.operator.delete(deleteParam) > 0;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public boolean clear() {
        return this.delete((DeleteParam) null);
    }

}
